<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

/**
 * Class EntityCommand
 * @package Prettus\Repository\Generators\Commands
 * @author Anderson Andrade <contato@andersonandra.de>
 */
class CrudCommand extends Command
{

    /**
     * The name of command.
     *
     * @var string
     */
    protected $name = 'make:crud {name=User}{--namespace=User}';

    /**
     * The description of command.
     *
     * @var string
     */
    protected $description = '新的代码生成器   name为当前功能命名，namespace主要职责为模块划分，默认为User,生成前，注意自己的表结构属于哪个模块';

    /**
     * @var Collection
     */
    protected $namespace;
    protected $replaceArr;//替换模板赋值

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the command.
     *
     * @return void
     * @see fire()
     */
    public function handle()
    {
        $this->laravel->call([$this, 'fire'], func_get_args());
    }

    /**
     * Execute the command.
     *
     * @return void
     */
    public function fire()
    {

        $name = ucfirst($this->argument('name'));

        $this->namespace = ucfirst($this->option('namespace'));

        $table = $this->option('table');
        if (is_null($table)) {
            $table = $this->ask('请输入对应的数据库表表名');
        }

        $fillable = $this->option('fillable');

        if(is_null($fillable)){
            $fillable = $this->ask('请输入需要生成的代码层级：1.model 2.params 3.repository 4.validator 5.service 6.controller'.PHP_EOL
                .'数字以逗号隔开,输入n代表都需要'
            );
        }
        if($fillable == 'n'){
            $fillable = '1,2,3,4,5,6';
        }

        $fillArr = explode(',',$fillable);

        $rules = $this->getRules($table,$name);

        $this->setReplaceArr(
            [
                'modelName'=>$name,
                'table'=>$table,
                'namespace'=>ucfirst($this->namespace),
                'modelIndex'=>lcfirst($name),
                'modelNameIndex'=>lcfirst($name)
            ]
        );

        in_array("5",$fillArr)&&$this->makeService($name, $rules);

        in_array("4",$fillArr)&&$this->makeValidate($name, $rules);

        in_array("2",$fillArr)&&$this->makeParams($name,$table);

        in_array("6",$fillArr)&&$this->makeController($name);

        in_array("3",$fillArr)&&$this->makeRepository($name);

        in_array("1",$fillArr)&&$this->makeModel($name, $table);
    }

    /**
     * @title 设置替换属性值
     * @param $key
     * @param $val
     */
    public function setReplaceVal($key,$val){
        $this->replaceArr[$key] = $val;
    }

    public function setReplaceArr(array $arr){
        foreach ($arr as $key=>$value){
            $this->replaceArr[$key] = $value;
        }
    }
    /**
     * title 获取替换后的模板
     */
    public function getReplacedTemplate($template){
        $pattern = "/{{([a-zA-Z][a-zA-Z0-9_]*)}}/";

        preg_match_all($pattern,$template,$matchArr);

        $find = $matchArr[0];

        foreach ($matchArr[1] as $key=> $value){
            $replace[$find[$key]] = isset($this->replaceArr[$value]) ? $this->replaceArr[$value]:$find[$key];
        }

        if(!empty($replace)){
            $template = strtr($template,$replace);
        }

        return $template;
    }

    /**
     * @title 获取相关验证规则
     */
    public function getRules($table,$name)
    {
        $tableInfo = $this->getTableInfo($table);

        if (!$tableInfo) {
            return false;
        }

        foreach ($tableInfo as $key => $value) {
            //id隐藏
            if($key =='id'){
                $key = lcfirst($name).'Id';
            }
            $rules[$this->convertUnderline($key)] = $this->getRulesByType($value);
        }

        return $rules;
    }

    public function getRulesByType($value)
    {

        if ($value['type'] == 'string') {
            $rule['rule'] = 'nullable|' . $value['type'] . '|max:' . $value['length'];
        }

        if ($value['type'] == 'text') {
            $rule['rule'] = 'nullable';
        }

        if ($value['type'] == 'tinyint' || $value['type'] == 'integer') {
            $rule['rule'] = 'nullable|integer';
        }

        $rule['rule'] = 'nullable|' . $value['type'];
        $rule['comment'] = $value['comment'];

        if($value['type'] == 'decimal'){
            $rule['rule']  =  'nullable|numeric';
        }

        return $rule;
    }



    /**
     * @title 获取表格基本信息
     * @return array
     * @throws \Doctrine\DBAL\Exception
     */
    public function getTableInfo($table)
    {

        $manager = DB::connection()->getDoctrineSchemaManager();
        $manager->getDatabasePlatform()
            ->registerDoctrineTypeMapping('enum', 'string');

        //兼容数据库带前缀
        $mysqlConfig = config('database.connections.mysql');
        if(!empty($mysqlConfig['prefix']))
            $table = $mysqlConfig['prefix'].$table;

        $tableDetail = $manager->listTableColumns($table);

        if (!$tableDetail) {
            echo '输入表名错误-'.$table;
            die;
        }
        //获取数据类型
        foreach ($tableDetail as $column) {
            $tableInfo[$column->getName()] =
                [
                    'type' => $column->getType()->getName(),
                    'length' => $column->getLength(),
                    'comment'=>$column->getComment()
                ];
        }
        //可以增加缓存
        return $tableInfo;
    }

    /**
     * The array of command arguments.
     *
     * @return array
     */
    public function getArguments()
    {
        return [
            [
                'name',
                InputArgument::REQUIRED,
                'The name of class being generated.',
                null
            ],
        ];
    }


    /**
     * The array of command options.
     *
     * @return array
     */
    public function getOptions()
    {
        return [
            [
                'fillable',
                null,
                InputOption::VALUE_OPTIONAL,
                'The fillable attributes.',
                null
            ],
            [
                'rules',
                null,
                InputOption::VALUE_OPTIONAL,
                'The rules of validation attributes.',
                null
            ],
            [
                'namespace',
                null,
                InputOption::VALUE_OPTIONAL,
                'Adds namespace reference to the repository.',
                null
            ],
            [
                'force',
                'f',
                InputOption::VALUE_NONE,
                'Force the creation if file already exists.',
                null
            ],
            [
                'table',
                't',
                InputOption::VALUE_REQUIRED,
                'table the creation if file already exists.',
                null
            ],
        ];
    }

    public function getStub($type)
    {
        return file_get_contents(resource_path("stubs/$type.stub"));
    }

    /**
     * @title 导入controller
     * @param $class
     * @param $namespace
     */
    protected function makeController($class)
    {
        $requestTemplate = $this->getReplacedTemplate($this->getStub('controller'));
        $path = $this->makePath("/Http/Controllers/Backend/".$this->namespace);

        $this->makeTemplate($path . "/{$class}Controller.php",$requestTemplate);

        $this->makeRoute($class);
    }

    public function makeRoute($class){

        $path = $this->makePath(base_path().'/routes/backend',0);
        echo $path;
        if (!file_exists($path . "/".lcfirst($this->namespace).".php")) {
            $router_file = 'route';
            $router_str = str_replace(
                ['{{routeModule}}', '{{modelNameIndex}}','{{namespace}}'],
                [lcfirst($this->namespace), lcfirst($class),$this->namespace],
                $this->getStub($router_file)
            );
            file_put_contents($path . "/".lcfirst($this->namespace).".php", $router_str, FILE_APPEND);
        }
    }

    /**
     * @title 导入验证
     * @param $class
     * @param $namespace
     */
    protected function makeValidate($class, $rules)
    {
        $rules = array_filter($rules);

        $field = array_keys($rules);
        $rulesArr = [];
        foreach ($rules as $key => $value) {
            $rulesArr[] = "        '" . $key . "' => " . "'" . $value['rule'] . "',//" . $value['comment'];
        }
        $rulesStr = implode(PHP_EOL, $rulesArr);

        $fieldArr = [];
        foreach ($field as $value) {
            $fieldArr[] = "            '" . $value . "',//" . $rules[$value]['comment'];
        }
        $fieldStr = implode(PHP_EOL, $fieldArr);

        $this->setReplaceArr(
            [
                'rules'=>$rulesStr,
                'field'=>$fieldStr
            ]
        );
        $requestTemplate = $this->getReplacedTemplate($this->getStub('validator'));

        $path = $this->makePath("/Http/Validators");
        $baseTemplate = $this->getStub('baseValidator');
        $this->makeTemplate($path . "/BaseValidator.php",$baseTemplate);

        $path = $this->makePath('/Http/Validators/'.$this->namespace);
        $this->makeTemplate($path . "/{$class}Validator.php",$requestTemplate);
    }

    public function makeParams($class, $table){
        $tableInfo = $this->getTableInfo($table);

        $attributeArr = [];
        foreach ($tableInfo as $key=>$value){
            if($key =='id')
                $key =  lcfirst($class).'Id';
            $attributeArr[] = "     /**" ;
            $attributeArr[] = "     * @var ".$value['type'];
            $attributeArr[] = "     * @title ".$value['comment'];
            $attributeArr[] = "     */ ";
            $attributeArr[] = "     public $" . $this->convertUnderline($key). ";";
        }

        $attributeStr = implode(PHP_EOL, $attributeArr);
        $fieldArr = [];

        $this->buildFiledStr($tableInfo,$class,$fieldArr);

        $fieldStr = implode(PHP_EOL, $fieldArr);

        $this->setReplaceArr(
            [
                'attributeStr'=>$attributeStr,
                'field'=>$fieldStr,
            ]
        );
        $requestTemplate = $this->getReplacedTemplate($this->getStub('params'));

        $path = $this->makePath("/Http/Params");
        $baseTemplate = $this->getStub('BaseParams');
        $this->makeTemplate($path . "/BaseParams.php",$baseTemplate);
        $this->makeTemplate($path . "/ParamsContract.php",$this->getStub('Params_contract'));
        $path = $this->makePath('/Http/Params/'.$this->namespace);
        $this->makeTemplate($path . "/{$class}Params.php",$requestTemplate);
    }

    //构建场景返回字符
    public function buildFiledStr(array $tableInfo,$class,&$fieldArr){
        $tableInfo = array_values($tableInfo);

        $count =count($tableInfo);
        if($count ==0){
            return $fieldArr;
        }
        $str='';
        for($i=0;$i<=5;$i++){
            if(count($tableInfo)){
                $column = array_shift($tableInfo);
                if($column['name'] =='id')
                    $column['name'] =  lcfirst($class).'Id';

                if($i ==0){
                    $str = "            '" . $this->convertUnderline($column['name'])."'" ;
                }else if($i<5&&$i>0){
                    $str .= ','."'".$this->convertUnderline($column['name'])."'";
                }else{
                    $str .= ','."'".$this->convertUnderline($column['name'])."'".',';
                }

            }else{
                $str&&$fieldArr[]=$str;
                return $fieldArr;
            }
        }
        $fieldArr[]=$str;

        return $this->buildFiledStr($tableInfo,$class,$fieldArr);
    }

    /**
     * @title 重新制作repository
     * @param $class
     */
    public function makeRepository($class)
    {
        $requestTemplate = $this->getReplacedTemplate($this->getStub('repository'));

        $path = $this->makePath("/Http/Repositories");
        $baseTemplate = $this->getStub('BaseEloquent');
        $this->makeTemplate($path . "/BaseRepository.php",$baseTemplate);

        $path = $this->makePath("/Http/Repositories/".$this->namespace);
        $this->makeTemplate($path . "/{$class}Repository.php",$requestTemplate);
    }

    /**
     * @title 制作service层
     * @param $class
     */
    protected function makeService($class, $rules)
    {

        $requestArr = [];
        foreach ($rules as $key => $value) {
            $requestArr[] = "            '" . $this->humpToLine($key) . "' => \$params->" . $key . ",//" . $value['comment'];
        }
        $requestFields = implode(PHP_EOL, $requestArr);
        $this->setReplaceArr(
            [
                'requestFields'=>$requestFields
            ]
        );
        $requestTemplate = $this->getReplacedTemplate($this->getStub('service'));

        $path = $this->makePath('/Http/Service/'.$this->namespace);

        $this->makeTemplate($path . "/{$class}Service.php",$requestTemplate);

    }


    public function makeModel($class, $table)
    {
        $requestTemplate = $this->getReplacedTemplate($this->getStub('model'));
        $path = $this->makePath("/Http/Model/".$this->namespace);
        $this->makeTemplate($path . "/{$class}.php",$requestTemplate);
    }

    /**
     * @param $path 路径
     * @param $isRelative 是否为相对路径
     * @title 生成目录
     * @return string
     */
    public function makePath(string $path,int $isRelative=1): string
    {
        $isRelative&&$path = app_path($path);
        if (!file_exists($path))
            mkdir($path, 0777, true);

        return $path;
    }

    /**
     * @title 生成
     * @param $fileName
     * @param $requestTemplate
     */
    public function makeTemplate(string $fileName,$requestTemplate){
        if (!file_exists($fileName)) {
            file_put_contents($fileName, $requestTemplate);
        }
    }

    public function convertUnderline($str)
    {
        return preg_replace_callback('/([-_]+([a-z]{1}))/i', function ($matches) {
            return strtoupper($matches[2]);
        }, $str);
    }

    public function humpToLine($str)
    {
        $str = preg_replace_callback('/([A-Z]{1})/', function ($matches) {
            return '_' . strtolower($matches[0]);
        }, $str);
        return ltrim($str, "_");

    }
}
